﻿using Inaction;
using Inaction.Criteria;
using Inaction.Resource.Properties;
using Inaction.Rule;
using Inaction.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Inaction.Mapping
{
    [Serializable]
    public class EntitySqlStatement : SqlStatement
    {
        #region 构造
        private EntitySqlStatement() { }
        #endregion

        #region 方法

        #region 生成Selecte语句
        public static SqlStatement GetSelectStatement<T>(CriteriaBase criteria = null) where T : Entity<T>, new()
        {
            return GetSelectStatement(typeof(T), criteria);
        }
        public static SqlStatement GetSelectStatementWithPropertyPara<T>(CriteriaBase criteria, params ParaValue[] paras)
            where T : Entity<T>, new()
        {
            return GetSelectStatementWithPropertyPara(typeof(T), criteria, paras);
        }
        public static SqlStatement GetSelectStatementWithColumnPara<T>(CriteriaBase criteria, params ParaValue[] paras)
          where T : Entity<T>, new()
        {
            return GetSelectStatementWithColumnPara(typeof(T), criteria, paras);
        }
        #endregion

        #endregion

        #region 生成分页Select语句
        public static SqlStatement GetSelectStatement<T>(int pageSize, int pageIndex, CriteriaBase criteria = null) where T : Entity<T>, new()
        {
            return GetSelectStatement(typeof(T), pageSize, pageIndex, criteria);
        }

        public static SqlStatement GetSelectStatement<T>(int pageSize, int pageIndex, Expression<Func<T, bool>> e) where T : Entity<T>, new()
        {
            var type = typeof(T);
            var statement = Linq.LinqHelper<T>.CreateSelectSql(e);
            var classInfo = ClassInfo.LoadInfo(type);
            if (classInfo.Properties.Count == 0) return null;

            string columns = string.Empty;
            columns = string.Join(",", classInfo.MappedProperties.Values.Where(p => p.Mapping).Select(p => p.Column.FormatName));
            if (classInfo.DeletedProperty != null && !statement.ParaValues.Any(p => p.Name == Inaction.DbProvider.Instance.ParaFix + PropertyKeyWord.Deleted))
            {
                if (!string.IsNullOrEmpty(statement.WhereText))
                {
                    statement.WhereText += " And ";
                }
                statement.WhereText += statement.WhereText + classInfo.DeletedProperty.Column.NotEqualsClause;
                statement.ParaValues.Add(new ParaValue { Name = classInfo.DeletedProperty.Column.ParameterName, Value = true });
            }
            var pagedSelecte = Inaction.DbProvider.Instance.GetPageSql(type, pageSize, pageIndex);
            statement.SelectSql = string.Format(pagedSelecte, columns, string.IsNullOrEmpty(statement.WhereText) ? string.Empty : " Where " + statement.WhereText);
            statement.WhereText = string.Empty;
            return statement;
        }
        #endregion

        #region 生成Insert语句
        public static SqlStatement GetInsertStatement<T>(T t, string rowCount = "") where T : Entity<T>, new()
        {
            var statement = new EntitySqlStatement();
            statement.StatementType = SqlStatementType.Insert;
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            var tableName = classInfo.FormatTableName;
            var columns = string.Empty;
            string valueStr = string.Empty;
            foreach (var property in classInfo.ColumnProperties.Values)
            {
                if (property.NeedUpdate == false) continue;
                var paraName = property.Column.ParameterName + rowCount;

                var value = property.GetFieldValue(t);
                if (value == null || string.IsNullOrEmpty(value.ToString())) continue;

                value = TypeConvert.ConvertToDbValue(value);
                statement.ParaValues.Add(new ParaValue
                {
                    Name = paraName,
                    Value = value
                });
                if (!string.IsNullOrEmpty(columns))
                {
                    columns += ",";
                }
                columns += property.Column.FormatName;
                if (!string.IsNullOrEmpty(valueStr))
                {
                    valueStr += ",";
                }
                valueStr += paraName;
            }
            statement.InsertText = "Insert Into " + tableName + " (" + columns + ") Values (" + valueStr + ")";
            return statement;
        }
        #endregion

        #region 生成Update语句
        public static SqlStatement GetUpdateStatement<T>(T t, string rowCount = "") where T : Entity<T>, new()
        {
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            var tableName = classInfo.FormatTableName;
            var statement = new EntitySqlStatement();
            statement.StatementType = SqlStatementType.Update;
            var setStr = string.Empty;
            var whereStr = classInfo.PrimaryKey.Column.FormatName + " = " + classInfo.PrimaryKey.Column.ParameterName + rowCount;
            statement.ParaValues.Add(new ParaValue
            {
                Name = classInfo.PrimaryKey.Column.Name + rowCount,
                Value = t.PrimaryKey
            });

            foreach (var property in classInfo.ColumnProperties.Values)
            {
                if (property.Column.IsPrimaryKey) continue;
                if (property.NeedUpdate == false) continue;
                if (!t.IsPropertyDirty(property) && !property.IsUpdaterProperty && !property.IsUpdateTimeProperty) continue;

                var columnName = property.Column.FormatName;
                var paraName = property.Column.ParameterName + rowCount;

                if (!string.IsNullOrEmpty(setStr))
                {
                    setStr += ",";
                }

                if (property.IsUpdateTimeProperty)
                {
                    var oldValue = t.GetOldValue(property);
                    var value = DateTime.Now;
                    if (t.CheckDirtyOnSave)
                    {
                        whereStr += " And " + columnName + " = " + paraName + "Where";
                        statement.ParaValues.Add(
                           new ParaValue
                           {
                               Name = paraName + "Where",
                               Value = oldValue
                           });
                        setStr += columnName + " = " + paraName;
                        statement.ParaValues.Add(
                           new ParaValue
                           {
                               Name = paraName,
                               Value = value
                           });
                    }
                    property.SetFieldValue(t, value);
                }

                else if (property.IsUpdaterProperty)
                {
                    var value = Inaction.Security.Principal.Instance.Identity.UserNumber;
                    property.SetFieldValue(t, value);
                    setStr += columnName + " = " + paraName;
                    statement.ParaValues.Add(
                       new ParaValue
                       {
                           Name = paraName,
                           Value = value
                       });
                }
                else
                {
                    object value = property.GetFieldValue(t);
                    if (value == null || string.IsNullOrEmpty(value.ToString()))
                        value = null;
                    else
                        value = TypeConvert.ConvertToDbValue(value);
                    if (value != null)
                    {
                        statement.ParaValues.Add(
                        new ParaValue
                        {
                            Name = paraName,
                            Value = value
                        });
                        setStr += columnName + " = " + paraName;
                    }
                    else
                    {
                        setStr += columnName + " = Null";
                    }
                }
            }
            if (string.IsNullOrEmpty(setStr)) return null;
            statement.UpdateText = " Update " + tableName + " Set " + setStr;
            statement.WhereText = " Where " + whereStr;
            return statement;
        }

        #endregion

        #region 生成Delete语句
        public static SqlStatement GetDeleteStatement<T>(T t, string rowCount = "") where T : Model<T>, new()
        {
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            var tableName = classInfo.FormatTableName;
            var statement = new EntitySqlStatement();
            statement.StatementType = SqlStatementType.Delete;
            var column = classInfo.PrimaryKey.Column.FormatName;
            var value = t.PrimaryKey;
            var paraName = t.ParaFix + classInfo.PrimaryKey.Column.Name + rowCount;
            statement.ParaValues.Add(new ParaValue
            {
                Name = paraName,
                Value = value
            });
            statement.DeleteText = "Delete From " + tableName + " ";
            statement.WhereText = " Where " + column + " = " + paraName;
            return statement;
        }

        public static SqlStatement GetLogicDeleteStatement<T>(T t, string rowCount = "") where T : Entity<T>, new()
        {

            var classInfo = ClassInfo.LoadInfo(typeof(T));
            if (classInfo.DeletedProperty != null)
            {
                var sql = GetUpdateStatement(t, rowCount);
                sql.StatementType = SqlStatementType.Delete;
                return sql;
            }
            else
            {
                return GetDeleteStatement(t, rowCount);
            }
        }
        #endregion

        #region 其他方法
        public static SqlStatement GetSaveStatement<T>(T t) where T : Entity<T>, new()
        {
            if (t.IsDeleted)
                return GetLogicDeleteStatement<T>(t);
            else if (t.IsNew)
                return GetInsertStatement(t);
            else if (t.IsSelfDirty)
                return GetUpdateStatement(t);
            return null;
        }
        #endregion        
    }
}